package dolphin.mt521.cam.ac.uk;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.Arrays;

import android.util.Log;

public class Duplex {
    
    private static byte[] encoded = null;
    
    public static void main(final File data) {
        Thread encode = new Thread(){
            public void run(){
                byte[] bData = Encode.convertToBytes(data);
                encoded = bData;
                Encode.encode(bData);
            }
        };
        encode.setDaemon(true);
        encode.start();
        
        Thread decode = new Thread(){
            public void run(){
                byte[] recorded = Microphone.mic();
                decode(recorded);
            }
        };
        decode.setDaemon(true);
        decode.start();
    }
    
    //DUPLEX DECODING//
    private static double backgroundLevel = 0.0;
    private static double dataVolume = 0.0;
    private static int chunkSize = Encode.getChunkSize();
    private static int freqIndex = 0;
    private static int spacing = Encode.getSpacing();
    private static int halfChunkSize = chunkSize/2;
    
    public static void decode(byte[] bTone) {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        double[] dTone = new double[halfChunkSize];
        double[] dTone2 = new double[halfChunkSize];        //for second chunk comparison
        double[] transformed = new double[halfChunkSize];
        double[] transformed2 = new double[halfChunkSize];  //for second chunk comparison
        long lFreq;
        byte bFreq;
        Log.i("Dolphin.decode","Sound Captured");
        backgroundLevel = Decode.getBackground(bTone);
        
        int position = 0;
        boolean noData = true;
        boolean chunk = true;  //which chunk to measure from now on? true if chunk1, false if chunk2
        int inc = chunkSize;   //used first chunk for background measure, can't be data
        while(noData) {
            for(int j = 0; j<halfChunkSize; j++) {
                dTone[j] = (double) bTone[inc+j];
            }
            transformed = Decode.fft(dTone);
            if (isData(transformed,false)) {
                if ((Decode.getFrequency(transformed)-300/spacing)!=encoded[position]) { //is this what I sent?
                    for(int j = 0; j<halfChunkSize; j++) {
                        dTone2[j] = (double) bTone[inc+j+halfChunkSize];    //get next chunk
                    }
                    transformed2 = Decode.fft(dTone2);
                    if(transformed[freqIndex]>transformed2[freqIndex]) {    //which amplitude at that point was stronger?
                        lFreq = (Decode.getFrequency(transformed)-300)/spacing;
                        bFreq = lFreq>127 ? (byte) (lFreq-256) : (byte) lFreq;
                        stream.write(bFreq);
                    }
                    else {
                        lFreq = (Decode.getFrequency(transformed2)-300)/spacing;
                        bFreq = lFreq>127 ? (byte) (lFreq-256) : (byte) lFreq;
                        stream.write(bFreq);
                        chunk=false;                                        //measure second chunk from here onwards
                    }
                    noData = false;                                     //exit loop
                }
                else {
                    position++;
                }
            }
            else {
                inc += halfChunkSize;
            }
        }

        if (chunk) {
            inc += chunkSize;
        }
        else {
            inc += chunkSize+halfChunkSize;
        }
        
        for(int i=inc; i<bTone.length; i+=chunkSize) {
            for(int j = 0; j<halfChunkSize; j++) {
                dTone[j] = (double) bTone[i+j];
            }
            transformed = Decode.fft(dTone);
            if (isData(transformed,false)){
                lFreq = (Decode.getFrequency(transformed)-300)/spacing;
                if (lFreq!=encoded[position]) {
                    bFreq = lFreq>127 ? (byte) (lFreq-256) : (byte) lFreq;
                    stream.write(bFreq);
                }
                else {
                    int indLower = (int) ((double)transformed.length*((double)(lFreq-spacing/2)/(Encode.getSampleRate()/2)));
                    int indHigher = (int) ((double)transformed.length*((double)(lFreq+spacing/2)/(Encode.getSampleRate()/2)));
                    Arrays.fill(transformed,indLower,indHigher,0.0d);
                    if (isData(transformed,true)) {
                        lFreq = (Decode.getFrequency(transformed)-300)/spacing;
                        bFreq = lFreq>127 ? (byte) (lFreq-256) : (byte) lFreq;
                        stream.write(bFreq);
                    }
                    position++;
                }
            }
        }
        Log.i("Dolphin.decode","Sound Decoded");
        byte[] result = stream.toByteArray();
        Output.saveFile(result,"dolphin(half)");
        Log.i("Dolphin.decode","New file saved to dolphin.txt");
    }
    
    //check if this chunk of sound contains an encoded byte
    //flag = FALSE for first call, TRUE for second call checking if it's a double transmission
    public static boolean isData(double[] amps, boolean flag) {
        double max = 0.0;
        for (int i=0; i<amps.length; i++) {
            if (amps[i]>max) {
                max = amps[i];
                freqIndex = i;
            }
        }
        if (flag) {
            if (max>dataVolume/2) {
                return true;
            }
            else {
                return false;
            }
        }
        else {
            if (max>backgroundLevel) {
                dataVolume = max;
                return true;
            }
            else {
                return false;
            }
        }
        
    }
}
